/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package dao;

import dao.exceptions.IllegalOrphanException;
import dao.exceptions.NonexistentEntityException;
import entities.User;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import entities.Institution;
import entities.Submission;
import java.util.ArrayList;
import java.util.Collection;
import entities.UserStats;
import entities.Contest;

/**
 *
 * @author santiagobaldrich
 */
public class UserJpaController {

    public UserJpaController()
    {
        emf = Persistence.createEntityManagerFactory("UNOJModelPU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager()
    {
        return emf.createEntityManager();
    }

    public void create(User user)
    {
        if (user.getSubmissionCollection() == null)
        {
            user.setSubmissionCollection(new ArrayList<Submission>());
        }
        if (user.getUserStatsCollection() == null)
        {
            user.setUserStatsCollection(new ArrayList<UserStats>());
        }
        if (user.getContestCollection() == null)
        {
            user.setContestCollection(new ArrayList<Contest>());
        }
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            Institution institution = user.getInstitution();
            if (institution != null)
            {
                institution = em.getReference(institution.getClass(), institution.getIdInstitution());
                user.setInstitution(institution);
            }
            Collection<Submission> attachedSubmissionCollection = new ArrayList<Submission>();
            for (Submission submissionCollectionSubmissionToAttach : user.getSubmissionCollection())
            {
                submissionCollectionSubmissionToAttach = em.getReference(submissionCollectionSubmissionToAttach.getClass(), submissionCollectionSubmissionToAttach.getIdSubmission());
                attachedSubmissionCollection.add(submissionCollectionSubmissionToAttach);
            }
            user.setSubmissionCollection(attachedSubmissionCollection);
            Collection<UserStats> attachedUserStatsCollection = new ArrayList<UserStats>();
            for (UserStats userStatsCollectionUserStatsToAttach : user.getUserStatsCollection())
            {
                userStatsCollectionUserStatsToAttach = em.getReference(userStatsCollectionUserStatsToAttach.getClass(), userStatsCollectionUserStatsToAttach.getIdUserStats());
                attachedUserStatsCollection.add(userStatsCollectionUserStatsToAttach);
            }
            user.setUserStatsCollection(attachedUserStatsCollection);
            Collection<Contest> attachedContestCollection = new ArrayList<Contest>();
            for (Contest contestCollectionContestToAttach : user.getContestCollection())
            {
                contestCollectionContestToAttach = em.getReference(contestCollectionContestToAttach.getClass(), contestCollectionContestToAttach.getIdContest());
                attachedContestCollection.add(contestCollectionContestToAttach);
            }
            user.setContestCollection(attachedContestCollection);
            em.persist(user);
            if (institution != null)
            {
                institution.getUserCollection().add(user);
                institution = em.merge(institution);
            }
            for (Submission submissionCollectionSubmission : user.getSubmissionCollection())
            {
                User oldUserOfSubmissionCollectionSubmission = submissionCollectionSubmission.getUser();
                submissionCollectionSubmission.setUser(user);
                submissionCollectionSubmission = em.merge(submissionCollectionSubmission);
                if (oldUserOfSubmissionCollectionSubmission != null)
                {
                    oldUserOfSubmissionCollectionSubmission.getSubmissionCollection().remove(submissionCollectionSubmission);
                    oldUserOfSubmissionCollectionSubmission = em.merge(oldUserOfSubmissionCollectionSubmission);
                }
            }
            for (UserStats userStatsCollectionUserStats : user.getUserStatsCollection())
            {
                User oldUserOfUserStatsCollectionUserStats = userStatsCollectionUserStats.getUser();
                userStatsCollectionUserStats.setUser(user);
                userStatsCollectionUserStats = em.merge(userStatsCollectionUserStats);
                if (oldUserOfUserStatsCollectionUserStats != null)
                {
                    oldUserOfUserStatsCollectionUserStats.getUserStatsCollection().remove(userStatsCollectionUserStats);
                    oldUserOfUserStatsCollectionUserStats = em.merge(oldUserOfUserStatsCollectionUserStats);
                }
            }
            for (Contest contestCollectionContest : user.getContestCollection())
            {
                User oldUserOfContestCollectionContest = contestCollectionContest.getUser();
                contestCollectionContest.setUser(user);
                contestCollectionContest = em.merge(contestCollectionContest);
                if (oldUserOfContestCollectionContest != null)
                {
                    oldUserOfContestCollectionContest.getContestCollection().remove(contestCollectionContest);
                    oldUserOfContestCollectionContest = em.merge(oldUserOfContestCollectionContest);
                }
            }
            em.getTransaction().commit();
        } finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public void edit(User user) throws IllegalOrphanException, NonexistentEntityException, Exception
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            User persistentUser = em.find(User.class, user.getIdUser());
            Institution institutionOld = persistentUser.getInstitution();
            Institution institutionNew = user.getInstitution();
            Collection<Submission> submissionCollectionOld = persistentUser.getSubmissionCollection();
            Collection<Submission> submissionCollectionNew = user.getSubmissionCollection();
            Collection<UserStats> userStatsCollectionOld = persistentUser.getUserStatsCollection();
            Collection<UserStats> userStatsCollectionNew = user.getUserStatsCollection();
            Collection<Contest> contestCollectionOld = persistentUser.getContestCollection();
            Collection<Contest> contestCollectionNew = user.getContestCollection();
            List<String> illegalOrphanMessages = null;
            for (Submission submissionCollectionOldSubmission : submissionCollectionOld)
            {
                if (!submissionCollectionNew.contains(submissionCollectionOldSubmission))
                {
                    if (illegalOrphanMessages == null)
                    {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Submission " + submissionCollectionOldSubmission + " since its user field is not nullable.");
                }
            }
            for (UserStats userStatsCollectionOldUserStats : userStatsCollectionOld)
            {
                if (!userStatsCollectionNew.contains(userStatsCollectionOldUserStats))
                {
                    if (illegalOrphanMessages == null)
                    {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain UserStats " + userStatsCollectionOldUserStats + " since its user field is not nullable.");
                }
            }
            for (Contest contestCollectionOldContest : contestCollectionOld)
            {
                if (!contestCollectionNew.contains(contestCollectionOldContest))
                {
                    if (illegalOrphanMessages == null)
                    {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Contest " + contestCollectionOldContest + " since its user field is not nullable.");
                }
            }
            if (illegalOrphanMessages != null)
            {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            if (institutionNew != null)
            {
                institutionNew = em.getReference(institutionNew.getClass(), institutionNew.getIdInstitution());
                user.setInstitution(institutionNew);
            }
            Collection<Submission> attachedSubmissionCollectionNew = new ArrayList<Submission>();
            for (Submission submissionCollectionNewSubmissionToAttach : submissionCollectionNew)
            {
                submissionCollectionNewSubmissionToAttach = em.getReference(submissionCollectionNewSubmissionToAttach.getClass(), submissionCollectionNewSubmissionToAttach.getIdSubmission());
                attachedSubmissionCollectionNew.add(submissionCollectionNewSubmissionToAttach);
            }
            submissionCollectionNew = attachedSubmissionCollectionNew;
            user.setSubmissionCollection(submissionCollectionNew);
            Collection<UserStats> attachedUserStatsCollectionNew = new ArrayList<UserStats>();
            for (UserStats userStatsCollectionNewUserStatsToAttach : userStatsCollectionNew)
            {
                userStatsCollectionNewUserStatsToAttach = em.getReference(userStatsCollectionNewUserStatsToAttach.getClass(), userStatsCollectionNewUserStatsToAttach.getIdUserStats());
                attachedUserStatsCollectionNew.add(userStatsCollectionNewUserStatsToAttach);
            }
            userStatsCollectionNew = attachedUserStatsCollectionNew;
            user.setUserStatsCollection(userStatsCollectionNew);
            Collection<Contest> attachedContestCollectionNew = new ArrayList<Contest>();
            for (Contest contestCollectionNewContestToAttach : contestCollectionNew)
            {
                contestCollectionNewContestToAttach = em.getReference(contestCollectionNewContestToAttach.getClass(), contestCollectionNewContestToAttach.getIdContest());
                attachedContestCollectionNew.add(contestCollectionNewContestToAttach);
            }
            contestCollectionNew = attachedContestCollectionNew;
            user.setContestCollection(contestCollectionNew);
            user = em.merge(user);
            if (institutionOld != null && !institutionOld.equals(institutionNew))
            {
                institutionOld.getUserCollection().remove(user);
                institutionOld = em.merge(institutionOld);
            }
            if (institutionNew != null && !institutionNew.equals(institutionOld))
            {
                institutionNew.getUserCollection().add(user);
                institutionNew = em.merge(institutionNew);
            }
            for (Submission submissionCollectionNewSubmission : submissionCollectionNew)
            {
                if (!submissionCollectionOld.contains(submissionCollectionNewSubmission))
                {
                    User oldUserOfSubmissionCollectionNewSubmission = submissionCollectionNewSubmission.getUser();
                    submissionCollectionNewSubmission.setUser(user);
                    submissionCollectionNewSubmission = em.merge(submissionCollectionNewSubmission);
                    if (oldUserOfSubmissionCollectionNewSubmission != null && !oldUserOfSubmissionCollectionNewSubmission.equals(user))
                    {
                        oldUserOfSubmissionCollectionNewSubmission.getSubmissionCollection().remove(submissionCollectionNewSubmission);
                        oldUserOfSubmissionCollectionNewSubmission = em.merge(oldUserOfSubmissionCollectionNewSubmission);
                    }
                }
            }
            for (UserStats userStatsCollectionNewUserStats : userStatsCollectionNew)
            {
                if (!userStatsCollectionOld.contains(userStatsCollectionNewUserStats))
                {
                    User oldUserOfUserStatsCollectionNewUserStats = userStatsCollectionNewUserStats.getUser();
                    userStatsCollectionNewUserStats.setUser(user);
                    userStatsCollectionNewUserStats = em.merge(userStatsCollectionNewUserStats);
                    if (oldUserOfUserStatsCollectionNewUserStats != null && !oldUserOfUserStatsCollectionNewUserStats.equals(user))
                    {
                        oldUserOfUserStatsCollectionNewUserStats.getUserStatsCollection().remove(userStatsCollectionNewUserStats);
                        oldUserOfUserStatsCollectionNewUserStats = em.merge(oldUserOfUserStatsCollectionNewUserStats);
                    }
                }
            }
            for (Contest contestCollectionNewContest : contestCollectionNew)
            {
                if (!contestCollectionOld.contains(contestCollectionNewContest))
                {
                    User oldUserOfContestCollectionNewContest = contestCollectionNewContest.getUser();
                    contestCollectionNewContest.setUser(user);
                    contestCollectionNewContest = em.merge(contestCollectionNewContest);
                    if (oldUserOfContestCollectionNewContest != null && !oldUserOfContestCollectionNewContest.equals(user))
                    {
                        oldUserOfContestCollectionNewContest.getContestCollection().remove(contestCollectionNewContest);
                        oldUserOfContestCollectionNewContest = em.merge(oldUserOfContestCollectionNewContest);
                    }
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex)
        {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0)
            {
                Integer id = user.getIdUser();
                if (findUser(id) == null)
                {
                    throw new NonexistentEntityException("The user with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws IllegalOrphanException, NonexistentEntityException
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            User user;
            try
            {
                user = em.getReference(User.class, id);
                user.getIdUser();
            } catch (EntityNotFoundException enfe)
            {
                throw new NonexistentEntityException("The user with id " + id + " no longer exists.", enfe);
            }
            List<String> illegalOrphanMessages = null;
            Collection<Submission> submissionCollectionOrphanCheck = user.getSubmissionCollection();
            for (Submission submissionCollectionOrphanCheckSubmission : submissionCollectionOrphanCheck)
            {
                if (illegalOrphanMessages == null)
                {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This User (" + user + ") cannot be destroyed since the Submission " + submissionCollectionOrphanCheckSubmission + " in its submissionCollection field has a non-nullable user field.");
            }
            Collection<UserStats> userStatsCollectionOrphanCheck = user.getUserStatsCollection();
            for (UserStats userStatsCollectionOrphanCheckUserStats : userStatsCollectionOrphanCheck)
            {
                if (illegalOrphanMessages == null)
                {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This User (" + user + ") cannot be destroyed since the UserStats " + userStatsCollectionOrphanCheckUserStats + " in its userStatsCollection field has a non-nullable user field.");
            }
            Collection<Contest> contestCollectionOrphanCheck = user.getContestCollection();
            for (Contest contestCollectionOrphanCheckContest : contestCollectionOrphanCheck)
            {
                if (illegalOrphanMessages == null)
                {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This User (" + user + ") cannot be destroyed since the Contest " + contestCollectionOrphanCheckContest + " in its contestCollection field has a non-nullable user field.");
            }
            if (illegalOrphanMessages != null)
            {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            Institution institution = user.getInstitution();
            if (institution != null)
            {
                institution.getUserCollection().remove(user);
                institution = em.merge(institution);
            }
            em.remove(user);
            em.getTransaction().commit();
        } finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public List<User> findUserEntities()
    {
        return findUserEntities(true, -1, -1);
    }

    public List<User> findUserEntities(int maxResults, int firstResult)
    {
        return findUserEntities(false, maxResults, firstResult);
    }

    private List<User> findUserEntities(boolean all, int maxResults, int firstResult)
    {
        EntityManager em = getEntityManager();
        try
        {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(User.class));
            Query q = em.createQuery(cq);
            if (!all)
            {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally
        {
            em.close();
        }
    }

    public User findUser(Integer id)
    {
        EntityManager em = getEntityManager();
        try
        {
            return em.find(User.class, id);
        } finally
        {
            em.close();
        }
    }

    public int getUserCount()
    {
        EntityManager em = getEntityManager();
        try
        {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<User> rt = cq.from(User.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally
        {
            em.close();
        }
    }

    public User getUserByHandle(String handle)
    {
        EntityManager em = null;
        User u = null;
        try
        {
            em = getEntityManager();
            Query q = em.createNamedQuery("User.findByHandle");
            q.setParameter("handle", handle);
            u = (User) q.getSingleResult();
        } catch (Exception e)
        {
            u = null;
        } finally
        {
            em.clear();
            em.close();
}
        return u;
    }

    public List<User> findUsersFromCountry(String name)
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            Query q = em.createQuery("select u from User u, Institution i, Country c "
                    + "Where u.institution = i and i.country = c "
                    + "and c.name = :name");
            q.setParameter("name", name);
            return (List<User>)q.getResultList();
        } catch (Exception e)
        {
            System.out.println("Exception: " + e.getMessage() );
            return null;
        }
    }

    public List<User> findUsersFromInstitution(String name)
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            Query q = em.createQuery("select u from User u, Institution i Where u.institution = i and i.name =:name ");
            q.setParameter("name", name);
            return (List<User>)q.getResultList();
        } catch (Exception e)
        {
            System.out.println("Exception: " + e.getMessage() );
            return null;
        }
    }
}
